//
//  WmcViewStateMachine.swift
//  YPWatermarkCamera
//
//  Created by yupao_ios_macmini06 on 2022/11/1.
//

import Foundation

// MARK: - [WmcViewStateMachineState]

enum WmcViewStateMachineState: Equatable {
    case none
    case view(String)
}

func == (lhs: WmcViewStateMachineState, rhs: WmcViewStateMachineState) -> Bool {
    switch (lhs, rhs) {
    case (.none, .none): return true
    case let (.view(lName), .view(rName)): return lName == rName
    default: return false
    }
}

// MARK: - [WmcViewStateMachine]

class WmcViewStateMachine {
    // Lifecycle

    // MARK: Init

    init(view: UIView, states: [String: UIView]?) {
        self.view = view
        viewStore = states ?? [String: UIView]()
    }

    convenience init(view: UIView) {
        self.init(view: view, states: nil)
    }

    // Internal

    let view: UIView

    fileprivate(set) var currentState: WmcViewStateMachineState = .none

    fileprivate(set) var lastState: WmcViewStateMachineState = .none

    // MARK: Add and remove view states

    func viewForState(_ state: String) -> UIView? {
        return viewStore[state]
    }

    func addView(_ view: UIView, forState state: String) {
        viewStore[state] = view
    }

    func removeViewForState(_ state: String) {
        viewStore[state] = nil
    }

    // MARK: Subscripting

    subscript(state: String) -> UIView? {
        get {
            return viewForState(state)
        }
        set(newValue) {
            if let value = newValue {
                addView(value, forState: state)
            } else {
                removeViewForState(state)
            }
        }
    }

    func transitionToState(_ state: WmcViewStateMachineState, animated: Bool = true, completion: (() -> Void)? = nil) {
        lastState = state
        queue.async { [weak self] in
            guard let strongSelf = self else { return }
            if state == strongSelf.currentState {
                return
            }
            strongSelf.queue.suspend()
            strongSelf.currentState = state
            let c: () -> Void = {
                strongSelf.queue.resume()
                completion?()
            }
            DispatchQueue.main.sync {
                switch state {
                case .none:
                    strongSelf.hideAllViews(animated: animated, completion: c)
                case let .view(viewKey):
                    strongSelf.showView(forKey: viewKey, animated: animated, completion: c)
                }
            }
        }
    }

    // Fileprivate

    fileprivate var viewStore: [String: UIView]
    fileprivate let queue = DispatchQueue(label: "com.aschuch.viewStateMachine.queue", attributes: [])

    // MARK: Private view updates

    fileprivate func showView(forKey state: String, animated: Bool, completion: (() -> Void)? = nil) {
        containerView.frame = view.bounds
        view.addSubview(containerView)
        let store = viewStore
        if let newView = store[state] {
            newView.alpha = animated ? 0.0 : 1.0
            let insets = (newView as? WmcStatefulPlaceholderView)?.placeholderViewInsets() ?? UIEdgeInsets()
            newView.translatesAutoresizingMaskIntoConstraints = false
            containerView.addSubview(newView)
            let metrics = ["top": insets.top, "bottom": insets.bottom, "left": insets.left, "right": insets.right]
            let views = ["view": newView]
            let hConstraints = NSLayoutConstraint.constraints(withVisualFormat: "|-left-[view]-right-|", options: [], metrics: metrics, views: views)
            let vConstraints = NSLayoutConstraint.constraints(withVisualFormat: "V:|-top-[view]-bottom-|", options: [], metrics: metrics, views: views)
            containerView.addConstraints(hConstraints)
            containerView.addConstraints(vConstraints)
        }
        let animations: () -> Void = {
            if let newView = store[state] {
                newView.alpha = 1.0
            }
        }
        let animationCompletion: (Bool) -> Void = { _ in
            for (key, view) in store {
                if !(key == state) {
                    view.removeFromSuperview()
                }
            }

            completion?()
        }
        animateChanges(animated: animated, animations: animations, completion: animationCompletion)
    }

    fileprivate func hideAllViews(animated: Bool, completion: (() -> Void)? = nil) {
        let store = viewStore
        let animations: () -> Void = {
            for (_, view) in store {
                view.alpha = 0.0
            }
        }
        let animationCompletion: (Bool) -> Void = { [weak self] _ in
            for (_, view) in store {
                view.removeFromSuperview()
            }
            self?.containerView.removeFromSuperview()
            completion?()
        }
        animateChanges(animated: animated, animations: animations, completion: animationCompletion)
    }

    fileprivate func animateChanges(animated: Bool, animations: @escaping () -> Void, completion: ((Bool) -> Void)?) {
        if animated {
            UIView.animate(withDuration: 0.3, animations: animations, completion: completion)
        } else {
            completion?(true)
        }
    }

    // Private

    private lazy var containerView: UIView = {
        let containerView = WmcPassthroughView(frame: .zero)
        containerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        containerView.backgroundColor = .clear
        return containerView
    }()
}

// MARK: - [WmcPassthroughView]

private class WmcPassthroughView: UIView {
    override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
        for view in subviews {
            if !view.isHidden, view.alpha > 0, view.isUserInteractionEnabled, view.point(inside: convert(point, to: view), with: event) {
                return true
            }
        }
        return false
    }
}
